package qunartest02;

import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

/**
 * @author: hanzheng.
 * @date: 18-11-22.
 */
public class FuturesUtil {
    private final static Logger logger = LoggerFactory.getLogger(FuturesUtil.class);

    /**
     * 当超时的时候获取结果如果当前这个{@code Future} 超时了那么会以{@code NULL}形式传递下去
     *
     * @param futures 异步结果
     * @param timeout 超时时间
     * @param unit    if {@code unit == null} unit将以默认值 {@code TimeUnit.MILLISECONDS} 的形式存在
     * @return 异步结果的 {@code List}
     */
    public static <T> List<T> getNullIfTimeOut(List<Future<T>> futures, long timeout, TimeUnit unit) {
        // check arguments
        Preconditions.checkArgument(futures != null, "futures is null");
        Preconditions.checkArgument(timeout >= 0, "timeout less than zero");
        unit = Optional.ofNullable(unit).orElse(TimeUnit.MILLISECONDS);

        List<T> results = Lists.newArrayListWithCapacity(futures.size());
        long mill = unit.toMillis(timeout);
        long allowCostTime = mill;
        Stopwatch stopwatch = Stopwatch.createStarted();
        for (Future<T> future : futures) {
            // clear stopwatch
            stopwatch.reset();
            stopwatch.start();
            T ftResult = null;
            try {
                ftResult = future.get(allowCostTime, TimeUnit.MILLISECONDS);
            } catch (TimeoutException e) {
                logger.error("future get result with timeout");
            } catch (Exception e) {
                logger.error("future get result with error", e);
            }
            results.add(ftResult);
            // compute next cost time
            long costTime = allowCostTime - stopwatch.elapsed(TimeUnit.MILLISECONDS);
            allowCostTime = costTime < 0 ? 0 : costTime;
        }
        return results;
    }

    /**
     * 当超时的时候获取结果如果当前这个{@code Future} 超时了那么会被忽略
     *
     * @param futures 异步结果
     * @param timeout 超时时间
     * @param unit    if {@code unit == null} unit将以默认值 {@code TimeUnit.MILLISECONDS} 的形式存在
     * @return 异步结果的 {@code List}
     */
    public static <T> List<T> ignoreIfTimeOut(List<Future<T>> futures, long timeout, TimeUnit unit) {
        return getNullIfTimeOut(futures, timeout, unit)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
}
